package com.xxlie.auth.security.auth.jwt;

import com.xxlie.auth.security.auth.JwtAuthenticationToken;
import com.xxlie.auth.domain.User;
import com.xxlie.auth.security.WebSecurityUtils;
import com.xxlie.auth.security.auth.jwt.extractor.TokenExtractor;
import com.xxlie.auth.security.model.UserContext;
import com.xxlie.auth.security.model.token.RawAccessJwtToken;
import com.xxlie.auth.service.UserService;
import com.xxlie.core.common.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.util.matcher.RequestMatcher;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * JwtTokenAuthenticationProcessingFilter
 *
 * @author xxlie.com
 * @since 2017/7/30
 */
public class JwtTokenAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {


    private final AuthenticationFailureHandler failureHandler;
    private final TokenExtractor tokenExtractor;

    private UserService userService;

    @Autowired
    public JwtTokenAuthenticationProcessingFilter(AuthenticationFailureHandler failureHandler,
                                                  TokenExtractor tokenExtractor, RequestMatcher matcher, UserService userService) {
        super(matcher);
        this.failureHandler = failureHandler;
        this.tokenExtractor = tokenExtractor;
        this.userService = userService;
    }

    @Override
    protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
        String tokenPayload = request.getHeader(WebSecurityUtils.AUTHENTICATION);
        return StringUtils.isNoneEmpty(tokenPayload) || super.requiresAuthentication(request, response);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException, IOException, ServletException {

//        String digestClient = request.getHeader(WebSecurityUtils.X_DIGEST);
//        if (digestClient == null || digestClient.isEmpty()) {
//            throw new JwtExpiredTokenException("The digest is missing from the '" + WebSecurityUtils.X_DIGEST + "' header");
//        }
//
//        String url = request.getRequestURL().toString();
//        if (request.getQueryString() != null) {
//            url += "?" + URLDecoder.decode(request.getQueryString(), Charset.forName("UTF-8").displayName());
//        }
//
//        String xOnceHeader = request.getHeader(WebSecurityUtils.X_ONCE);
//        if (xOnceHeader == null || xOnceHeader.isEmpty()) {
//            throw new JwtExpiredTokenException("The date is missing from the '" + WebSecurityUtils.X_ONCE + "' header");
//        }
//        String body;
//        if ("POST".equals(request.getMethod()) || "PUT".equals(request.getMethod()) || "PATCH".equals(request.getMethod())) {
//            WrappedRequest wrappedRequest = new WrappedRequest(request);
//            body = request.getMethod().concat(wrappedRequest.getBody()).concat(url).concat(xOnceHeader);
//        } else {
//            body = request.getMethod().concat(url).concat(xOnceHeader);
//        }

        String tokenPayload = request.getHeader(WebSecurityUtils.AUTHENTICATION);
        RawAccessJwtToken token = new RawAccessJwtToken(tokenExtractor.extract(tokenPayload));

//        token.setBody(body);
//        token.setDigestClient(digestClient);

        return getAuthenticationManager().authenticate(new JwtAuthenticationToken(token));

    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
                                            Authentication authentication) throws IOException, ServletException {

        UserContext userContext = (UserContext) authentication.getPrincipal();

        User user = userService.getByUsername(userContext.getUsername()).orElseThrow(() -> new UsernameNotFoundException("User not found: " + userContext.getUsername()));
        userContext.setUser(user);


        SecurityContext context = SecurityContextHolder.createEmptyContext();

        context.setAuthentication(authentication);
        SecurityContextHolder.setContext(context);
        chain.doFilter(request, response);
    }

    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
                                              AuthenticationException failed) throws IOException, ServletException {
        SecurityContextHolder.clearContext();
        failureHandler.onAuthenticationFailure(request, response, failed);
    }
}
